package com.riaevolution.components.ModuleFactory
{
	import flash.events.Event;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.xml.XMLDocument;
	import flash.xml.XMLNode;
	
	import mx.collections.ArrayCollection;
	import mx.events.ModuleEvent;
	import mx.modules.ModuleManager;
	
	/**
	* Application specific Factory Pattern implementation which defines an API that handles the podCreation
	*/
	
	public class ModuleFactoryManager
	{
		
		/**
		 * Module path prefix or directory 
		 * 
		 **/
		public static var modulesPath:String = "";
		private static var instance:ModuleFactoryManager;
		private static var allowInstantiation:Boolean = false;
		public static var configPath:String = "ModuleFactoryManagerSource.xml";
		public static var group:String = "default";
		
		private var _podColl:ArrayCollection = new ArrayCollection();
		private var _ispending:Boolean;
		
		private var _policyGroups:Object;
		
		public static function getInstance():ModuleFactoryManager{
			if (instance == null) {
				allowInstantiation = true;
				instance = new ModuleFactoryManager();
				allowInstantiation = false;
			}
			return instance;
		}
		public static function load(xmluri:String):void {
			instance.pending = true;
			configPath = xmluri;
			var loader:URLLoader = new URLLoader();
			loader.addEventListener(Event.COMPLETE, getInstance().handleComplete);
			loader.load(new URLRequest(configPath));
			trace("loading.. "+configPath);
			if(!instance._policyGroups) instance._policyGroups = new Object();
		}
		
		public function ModuleFactoryManager()
		{
			if (!allowInstantiation) {
				throw new Error("Error: Instantiation failed: Use ModuleFactoryManager.getInstance() instead of new.");
			}
		}
		
		private function handleComplete(event:Event):void {
			
			var loader:URLLoader = URLLoader(event.target);
			var xDoc:XMLDocument = new XMLDocument();
			xDoc.ignoreWhite = true;
			xDoc.parseXML((new XML(loader.data)).toString());
			
			var initindex:int = xDoc.firstChild.attributes.initialpod ? parseInt(xDoc.firstChild.attributes.initialpod): -1;
			ModuleFactoryManager.modulesPath = xDoc.firstChild.attributes.basepath;
			
			var data:Array = new Array();
			var t:int = 0;
			while (t<xDoc.firstChild.childNodes.length) {
				
				var node:XMLNode = xDoc.firstChild.childNodes[t];
				switch(node.nodeName.toLocaleLowerCase()){
					case 'policy'://handle the policy creation
					createPolicy(node);
					break;
					case 'module'://handle the module creation
					data.push(createItemNode(node));
					break;
				}
				t++;
			}
			dataProvider = new ArrayCollection(data);
			if(!isNaN(initindex) && initindex>=0){
				var p:ModuleItem = dataProvider.getItemAt(initindex) as ModuleItem;
				ModuleFactoryManager.loadModule(p, true);
			} else {
				pending = false;
			}
			instance._podColl.filterFunction = instance.checkCollection;
			instance._podColl.refresh();
		}
		private function checkCollection(item:IModuleItem):Boolean {
			return checkPolicy(item.getName());
		}
		
		private function checkPolicy(name:String):Boolean {
			var acl:Object = getInstance()._policyGroups;
			var group:String = ModuleFactoryManager.group;
			//trace("Validating for: "+name+" into G: "+group);
			if(!acl[group]){
				throw new Error("Policy Group {"+name+"} Not available");
				return false;
			}
			else {
				if(acl[group].restricted) {
					var col:Array = acl[group].collection as Array;
					if(col.length>0) {
						for(var t:String in col) {
							if( String(col[t]).toLocaleLowerCase() == name.toLocaleLowerCase() ) {
								return true;
							}
						}
						return false;
					}
					else {
						return false;
					}
				}
				else {
					return true;
				}
			}
			return false;
		}
		private function createPolicy(node:XMLNode):void {
			var gname:String = node.attributes.group;
			var access:String = node.attributes.access;
			if(!_policyGroups) _policyGroups = new Object();
			if(!_policyGroups[gname]) {
				_policyGroups[gname] = new Object();
				_policyGroups[gname].restricted = false;
				_policyGroups[gname].collection = new Array();
				_policyGroups[gname].group = gname;
			}
			if(access!="*") {
				_policyGroups[gname].restricted = true;
				_policyGroups[gname].collection = access.split(",");
			}
		}
		private function createItemNode(node:XMLNode):ModuleItem {
			var mi:ModuleItem = new ModuleItem();
			mi.label = node.attributes.label;
			mi.autocreate = Boolean(node.attributes.autocreate == 'true');
			mi.name = node.attributes.name;
			mi.uri = node.attributes.uri;
			mi.width = Number(node.attributes.width);
			mi.height = Number(node.attributes.height);
			return mi;
		}
		
		public static function loadModule(moduleItem:IModuleItem, autoinit:Boolean = false, readyListener:Function=null, errorListener:Function=null):void {
			var pod:ModuleItem = moduleItem as ModuleItem;
			if(!getInstance().checkPolicy(pod.name)) {
				throw new Error("Tienes restringido el acceso a este modulo\n"+pod.name);
			}
			getInstance().pending = true;
			pod.module = ModuleManager.getModule(ModuleFactoryManager.modulesPath+pod.uri);
			if(!pod.module.hasEventListener(ModuleEvent.ERROR)) {
				pod.module.addEventListener(ModuleEvent.ERROR,
					function (event:ModuleEvent):void {
						getInstance().pending = pod.moduleReady = false;
						pod.module.dispatchEvent(
							new ModuleFactoryEvent(ModuleFactoryEvent.MODULE_ERROR,moduleItem)
						);
						if(pod.module.hasEventListener(ModuleFactoryEvent.MODULE_READY))
						pod.module.removeEventListener(ModuleFactoryEvent.MODULE_READY,readyListener);
						if(pod.module.hasEventListener(ModuleFactoryEvent.MODULE_ERROR))
						pod.module.removeEventListener(ModuleFactoryEvent.MODULE_ERROR,errorListener);
					}
				);
			}
			if(readyListener != null) pod.module.addEventListener(ModuleFactoryEvent.MODULE_READY,readyListener,false,0,true);
			if(errorListener != null) pod.module.addEventListener(ModuleFactoryEvent.MODULE_ERROR,errorListener,false,0,true);
			
			if(!pod.module.hasEventListener(ModuleEvent.READY)) {
				pod.module.addEventListener(ModuleEvent.READY,
					function (event:ModuleEvent):void {
						getInstance().pending = false;
						pod.moduleReady = true;
						trace("@Module loaded");
						if(pod.autocreate || autoinit) {
							trace("@autocreate the module");
							try {
								pod.component = pod.module.factory.create();
							}
							catch(e:Error) {
								trace("@Error creating the module "+pod.name);
							}
						}
						pod.module.dispatchEvent(
							new ModuleFactoryEvent(ModuleFactoryEvent.MODULE_READY,moduleItem)
						);
						if(pod.module.hasEventListener(ModuleFactoryEvent.MODULE_READY))
						pod.module.removeEventListener(ModuleFactoryEvent.MODULE_READY,readyListener);
						if(pod.module.hasEventListener(ModuleFactoryEvent.MODULE_ERROR))
						pod.module.removeEventListener(ModuleFactoryEvent.MODULE_ERROR,errorListener);
					}
				);
			}
			pod.module.load();
		}
		//@todo handle the loading logic inside the main object.
		private function handleModuleError(event:ModuleEvent):void
		{
			//getInstance().pending = pod.moduleReady = false;
			//trace("@Problems loading the module :"+pod.podName+" at "+pod.uri);
		}
		//@todo handle the loading logic inside the main object.
		private function handleModuleLoaded(event:ModuleEvent):void
		{
			//getInstance().pending = pod.moduleReady = false;
			//trace("@Problems loading the module :"+pod.podName+" at "+pod.uri);
		}
		/**
		* Dataprovider getter/setter
		*
		*/
		
		[Bindable]
		public function get dataProvider():ArrayCollection {
			return _podColl;
		}
		public function set dataProvider(array:ArrayCollection):void {
			_podColl = array;
		}
		
		[Bindable]
		public function get groupAccess():String {
			return ModuleFactoryManager.group;
		}
		public function set groupAccess(groupName:String):void {
			ModuleFactoryManager.group = groupName;
			_podColl.filterFunction = checkCollection;
			_podColl.refresh();
		}
		
		/**
		* Status indicator por the main object.
		*/
		[Bindable]
		public function get pending():Boolean {
			return _ispending;
		}
		public function set pending(bol:Boolean):void {
			_ispending = bol;
		}
		/**
		* Single pod loader method if, the pod not loaded they will try to load.
		**/
		public static function getModule(name:String):ModuleItem {
			if(!getInstance().checkPolicy(name)) {
				throw new Error("Tienes restringido el acceso a este modulo\n"+name);
			}
			var col:ArrayCollection = getInstance().dataProvider;
			for(var t:int=0; t<col.length; t++){
				if(ModuleItem(col.getItemAt(t)).name == name && ModuleItem(col.getItemAt(t)).module.loaded) {
					return ModuleItem(col.getItemAt(t));
				}
			}
			return null;
		}
	}
}